﻿using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace SimJsonRpc
{
    public class RpcHttpServerMiddleware
    {
        public const string KEY_APP_ID = "app_id";
        public const string KEY_FORMAT = "format";
        public const string KEY_METHOD = "method";
        public const string KEY_TIMESTAMP = "timestamp";
        public const string KEY_VERSION = "version";
        public const string KEY_SIGN_TYPE = "sign_type";
        public const string KEY_ACCESS_TOKEN = "auth_token";
        public const string KEY_SIGN = "sign";
        public const string KEY_TERMINAL_TYPE = "terminal_type";
        public const string KEY_TERMINAL_INFO = "terminal_info";
        public const string KEY_PROD_CODE = "prod_code";
        public const string KEY_NOTIFY_URL = "notify_url";
        public const string KEY_CHARSET = "charset";
        public const string KEY_ENCRYPT_TYPE = "encrypt_type";
        public const string KEY_PARAMS = "params";
        public const string KEY_APP_AUTH_TOKEN = "app_auth_token";
        public const string KEY_RETURN_URL = "return_url";

        private readonly RequestDelegate _next;
        private readonly IServiceProvider _serviceProvider;
        private readonly RpcHttpServerOptions _rpchttpServerOptions;


        public RpcHttpServerMiddleware(RequestDelegate next, IServiceProvider serviceProvider, RpcHttpServerOptions rpchttpServerOptions)
        {
            _next = next;
            _serviceProvider = serviceProvider;
            _rpchttpServerOptions = rpchttpServerOptions;
        }

        public RpcHttpServerMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task InvokeAsync(HttpContext context)
        {
            if (context.Request.Path == "/")
            {
                context.Response.ContentType = "text/plain; charset=utf-8";
                await context.Response.WriteAsync("程序已启动！", Encoding.UTF8);
            }
            else if (context.Request.Path == _rpchttpServerOptions.Path)
            {
                JObject response;
                try
                {
                    context.Response.Headers["Access-Control-Allow-Origin"] = "*";
                    context.Response.Headers["Access-Control-Allow-Methods"] = "GET, POST";
                    context.Response.Headers["Access-Control-Allow-Headers"] = "Content-Type";
                    context.Response.Headers["Access-Control-Max-Age"] = "31536000";
                    if (context.Request.Method != "GET" && context.Request.Method != "POST") return;
                    JObject request = null;
                    IDictionary<string, string> textParams = new Dictionary<string, string>();
                    IDictionary<string, FileItem> fileParams = new Dictionary<string, FileItem>();
                    if (context.Request.Query != null)
                    {
                        var queryCollection = context.Request.Query;
                        foreach (var item in queryCollection)
                        {
                            if (textParams.Keys.Contains(item.Key))
                            {
                                textParams[item.Key] = item.Value;
                            }
                            else
                            {
                                textParams.Add(item.Key, item.Value);
                            }
                        }
                    }
                    if (context.Request.HasFormContentType)
                    {
                        if (context.Request.HasFormContentType && context.Request.Form.Files.Count > 0)
                        {
                            var fileCollection = context.Request.Form.Files;
                            foreach (var item in fileCollection)
                            {
                                using (var ms = new MemoryStream())
                                {
                                    item.CopyTo(ms);
                                    var fileBytes = ms.ToArray();
                                    fileParams.Add(item.Name, new FileItem(item.FileName, fileBytes));
                                }
                            }
                        }
                        else
                        {
                            var formCollection = context.Request.Form;
                            foreach (var item in formCollection)
                            {
                                if (textParams.Keys.Contains(item.Key))
                                {
                                    textParams[item.Key] = item.Value;
                                }
                                else
                                {
                                    textParams.Add(item.Key, item.Value);
                                }
                            }
                        }
                    }
                    if (context.Request.Method == "GET")
                    {
                        if (textParams.Keys.Contains(KEY_METHOD) && textParams.Keys.Contains(KEY_PARAMS))
                        {
                            request = JObject.FromObject(textParams);
                            request[KEY_PARAMS] = JsonConvert.DeserializeObject(textParams[KEY_PARAMS]) as JToken;
                        }
                    }
                    else if (context.Request.Method == "POST")
                    {
                        // 如果没有文件参数，则走普通POST请求
                        if (fileParams == null || fileParams.Count == 0)
                        {
                            if (textParams.Keys.Contains(KEY_METHOD) && textParams.Keys.Contains(KEY_PARAMS))
                            {
                                request = JObject.FromObject(textParams);
                                request[KEY_PARAMS] = JsonConvert.DeserializeObject(textParams[KEY_PARAMS]) as JToken;
                            }
                            else
                            {
                                string documentContents;
                                using (Stream receiveStream = context.Request.Body)
                                {
                                    using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
                                    {
                                        documentContents = readStream.ReadToEnd();
                                    }
                                }
                                if (!string.IsNullOrEmpty(documentContents))
                                {
                                    request = JsonConvert.DeserializeObject(documentContents) as JObject;
                                }
                            }
                        }
                        else
                        {
                            //上传文件
                            request = JObject.FromObject(textParams);
                            request[KEY_PARAMS] = JObject.FromObject(fileParams);
                        }
                    }

                    if (request == null)
                    {
                        response = CreateErrorResponse(null, 500, "缺少有效的入参");
                    }
                    else
                    {
                        response = (_serviceProvider.GetService(typeof(IRpcInvoker)) as IRpcInvoker).InvokeRequestAsync(request, _serviceProvider);
                    }
                }
                catch (Exception ex)
                {
                    response = CreateErrorResponse(null, 500, ex.Message);
                }
                context.Response.ContentType = "application/json";
                await context.Response.WriteAsync(response.ToString(), Encoding.UTF8);
            }
            else
            {
                context.Response.StatusCode = 404;
                await context.Response.WriteAsync("");
            }
        }

        private JObject CreateResponse(string id)
        {
            JObject response = new JObject();
            response["jsonrpc"] = "2.0";
            response["id"] = id;
            return response;
        }

        private JObject CreateErrorResponse(string id, int code, string message, JObject data = null)
        {
            JObject response = CreateResponse(id);
            JObject responseError = new JObject();
            responseError["code"] = code;
            responseError["message"] = message;
            if (data != null)
                responseError["data"] = data;
            response["error"] = responseError;
            return response;
        }
    }
}
